<script lang="ts" module>
    import { getContext, setContext, untrack } from "svelte";
    import { Dropdown } from "../Dropdown";
    import Icon from "../Common/Icons/Icon.svelte";

    export const DatepickerContextKey = Symbol("DatepickerContextKey");
    export const useDatepickerContext = () => getContext(DatepickerContextKey);
</script>

<script lang="ts">
    import dayjs from "dayjs";
    import type { Dayjs } from "dayjs";
    import weekOfYear from "dayjs/plugin/weekOfYear";
    import advancedFormat from "dayjs/plugin/advancedFormat";
    import quarterOfYear from "dayjs/plugin/quarterOfYear";
    import weekYear from "dayjs/plugin/weekYear";
    import type { DatepickerProps, DatepickerStore } from "./Datepicker";
    import { useClassList } from "../utils/useClassList";
    import DatePane from "./DatePane.svelte";
    import MonthPane from "./MonthPane.svelte";
    import { Value } from "../Common/Value";
    import DateTimePane from "./DateTimePane.svelte";
    import MonthRangePane from "./MonthRangePane.svelte";
    import DateRangePane from "./DateRangePane.svelte";
    import DateTimeRangePane from "./DateTimeRangePane.svelte";
    import { useFormItemContext } from "../FormItem/FormItem.svelte";
    import QuarterPane from "./QuarterPane.svelte";
    import QuarterRangePane from "./QuarterRangePane.svelte";
    const { isDayjs } = dayjs;
    dayjs.extend(advancedFormat);
    dayjs.extend(weekYear);
    dayjs.extend(weekOfYear);
    dayjs.extend(quarterOfYear);

    let {
        type = "date",
        value = $bindable(
            type === "dateRange" || type === "monthRange" || type === "dateTimeRange" || type === "yearRange" || type === "weekRange" || type === "quarterRange" ? [] : undefined
        ) as DatepickerProps["value"],
        ...props
    }: DatepickerProps = $props();
    const ctx = useFormItemContext();
    let visible = $state(false);
    let v = $state();
    let format = props.format ?? "YYYY-MM-DD";
    if (type === "month" || type === "monthRange") {
        format = props.format ?? "YYYY-MM";
    }
    if (type === "year" || type === "yearRange") {
        format = props.format ?? "YYYY";
    }
    if (type === "week" || type === "weekRange") {
        format = props.format ?? "gggg-wo";
    }
    if (type === "quarter" || type === "quarterRange") {
        format = props.format ?? "YYYY-[Q]Q";
    }
    if (type === "dateTime" || type === "dateTimeRange") {
        format = props.format ?? "YYYY-MM-DD HH:mm:ss";
    }

    const now = dayjs();
    const next = dayjs().add(1, "month");
    const store = $state({
        currentMonth: [now.toDate(), next.toDate()],
        range: [],
        hoverDate: undefined,
    } as DatepickerStore);
    const align = props.align ?? "bottomLeft";
    $effect(() => {
        let val: any = $state.snapshot(value);
        let currentMonth: Date[] = [];
        untrack(() => {
            if (val) {
                if (typeof val === "string") {
                    val = dayjs(val).toDate();
                    const prev = new Date(val);
                    const next = new Date(val);
                    next.setMonth(next.getMonth() + 1);
                    currentMonth = [prev, next];
                } else if (isDayjs(val)) {
                    val = dayjs(val).toDate();
                    const prev = new Date(val);
                    const next = new Date(val);
                    next.setMonth(next.getMonth() + 1);
                    currentMonth = [prev, next];
                } else {
                    let prev: Date = new Date();
                    let next: Date = new Date();
                    if (val instanceof Array) {
                        if ((val[0] && typeof val[0] === "string") || isDayjs(val[0])) {
                            val[0] = dayjs(val[0]).toDate();
                        }
                        if ((val[1] && typeof val[1] === "string") || isDayjs(val[1])) {
                            val[1] = dayjs(val[1]).toDate();
                        }

                        prev = new Date(dayjs(val[0]).toDate());
                        next = new Date(dayjs(val[1]).toDate());
                    }
                    if (type === "month" && isDayjs(val)) {
                        prev = val.toDate();
                        next = new Date(val.toDate());
                    }

                    if (dayjs(prev).format("YYYY-MM") === dayjs(next).format("YYYY-MM")) {
                        next.setMonth(next.getMonth() + 1);
                    }
                    if ((type === "quarterRange" || type === "yearRange") && dayjs(prev).format("YYYY") === dayjs(next).format("YYYY")) {
                        next.setFullYear(next.getFullYear() + 1);
                    }
                    currentMonth = [prev, next];
                }
                if (type === "dateRange" || type === "dateTimeRange" || type === "weekRange" || type === "quarterRange") {
                    store.range = val;
                }
            } else {
                currentMonth = [now.toDate(), next.toDate()];
                if (type === "quarterRange" || type === "yearRange") {
                    currentMonth[1] = dayjs().add(1, "year").toDate();
                }
            }
            // 粘连时根据第一个month往后加一个月
            if (props.stick) {
                currentMonth[1] = new Date(currentMonth[0]);
                if (type === "quarterRange") {
                    currentMonth[1].setFullYear(currentMonth[1].getFullYear() + 1);
                } else {
                    currentMonth[1].setMonth(currentMonth[1].getMonth() + 1);
                }
            }
            currentMonth[0].setDate(1);
            currentMonth[1].setDate(1);
        });
        store.currentMonth = currentMonth;
        v = val;
    });

    const classList = $derived(
        useClassList(props, "cm-date-picker", {
            [`cm-date-picker-${props.size}`]: props.size,
            [`cm-date-picker-type-${type}`]: type,
            "cm-date-picker-disabled": props.disabled,
            "cm-date-picker-clearable":
                !props.disabled &&
                props.clearable &&
                (((type === "dateRange" || type === "monthRange" || type === "dateTimeRange" || type === "yearRange" || type === "weekRange" || type === "quarterRange") &&
                    (value as Dayjs[][0]) &&
                    (value as Dayjs[][1])) ||
                    value),
        })
    );

    const onClear = () => {
        value = type === "dateRange" || type === "monthRange" || type === "dateTimeRange" || type === "yearRange" || type === "weekRange" || type === "quarterRange" ? ([] as Dayjs[]) : undefined;
        if (type === "dateRange" || type === "weekRange") {
            store.range = [];
        }
        props.onchange?.(value);
        ctx?.onFieldChange?.();
    };

    // 点击选择日期
    const onSelectDate = (day: Date, name: string) => {
        const va = new Date(day);
        if (type === "month" || type === "monthRange" || type === "year" || type === "yearRange" || type === "quarter") {
            va.setDate(1);
            va.setHours(0);
            va.setMinutes(0);
            va.setSeconds(0);
            va.setMilliseconds(0);
        }
        if (type === "year" || type === "yearRange") {
            va.setMonth(0);
        }
        if (type === "dateTime" || type === "dateTimeRange") {
            let val = v as any;
            if (type === "dateTimeRange") {
                val = val && val.length ? val[store.range.length === 1 ? 1 : 0] : store.currentMonth[store.range.length === 1 ? 1 : 0];
            } else {
                val = val ? val : store.currentMonth[store.range.length === 1 ? 1 : 0];
            }
            va.setHours(val.getHours());
            va.setMinutes(val.getMinutes());
            va.setSeconds(val.getSeconds());
        }
        const now = new Date();
        const origin: any =
            $state.snapshot(v) ||
            (type === "monthRange" || type === "dateRange" || type === "dateTimeRange" || type === "yearRange" || type === "weekRange" || type === "quarterRange" ? [now, now] : now);
        if ((type === "dateRange" || type === "dateTimeRange" || type === "weekRange" || type === "quarterRange") && origin instanceof Array && !origin.length) {
            origin.push(now);
            origin.push(now);
        }
        let newVal;
        if (name === "start" && origin instanceof Array) {
            newVal = [va, origin[1]];
        } else if (name === "end" && origin instanceof Array) {
            newVal = [origin[0], va];
        } else {
            newVal = va;
        }

        // 选择及改变的可能存在未选择的日期
        if (type === "monthRange" || type === "yearRange") {
            if (newVal instanceof Array) {
                newVal = newVal.map((v) => {
                    if (!v) {
                        if (type === "monthRange") {
                            return dayjs(dayjs().format("YYYY-MM-01 00:00:00")).toDate();
                        }
                        if (type === "yearRange") {
                            return dayjs(dayjs().format("YYYY-01-01 00:00:00")).toDate();
                        }
                    }
                    return v;
                });
            }
        }

        if (newVal instanceof Array && newVal[0] && newVal[1] && newVal[0].getTime() > newVal[1].getTime()) {
            newVal.reverse();
        }
        // dateRange特殊处理
        if (type === "dateRange" || type === "dateTimeRange" || type === "weekRange" || type === "quarterRange") {
            const range = store.range;
            let newRange: Date[] = [];
            // 上次已经选择
            if ((range[0] && range[1]) || (!range[0] && !range[1])) {
                newRange = [va];
                store.hoverDate = new Date(va);
            }
            if (range[0] && !range[1]) {
                if (isOutRange(range[0], va)) {
                    return;
                }
                newRange = [range[0], va];
                if (newRange[0].getTime() > newRange[1].getTime()) {
                    newRange.reverse();
                    // dateTImeRange时需要切换currentMonth的时间
                    const tmp = new Date();
                    copyHMS(tmp, store.currentMonth[0]);
                    copyHMS(store.currentMonth[0], store.currentMonth[1]);
                    copyHMS(store.currentMonth[1], tmp);
                    store.currentMonth = [...store.currentMonth];
                }
                value = [dayjs(newRange[0]), dayjs(newRange[1])];
                props.onchange?.([dayjs(newRange[0]), dayjs(newRange[1])]);
                ctx?.onFieldChange?.();
                if (type === "dateRange" || type === "weekRange" || type === "quarterRange") {
                    visible = false;
                }
            }
            store.range = newRange;
            return;
        }
        if (newVal instanceof Array) {
            newVal = newVal.map((v) => v && dayjs(v));
        } else {
            newVal = dayjs(newVal);
        }
        value = newVal;
        props.onchange?.(newVal);
        ctx?.onFieldChange?.();
        if (type === "date" || type === "week" || type === "quarter") {
            visible = false;
        }
    };

    const copyHMS = (target: Date, source: Date) => {
        target.setHours(source.getHours());
        target.setMinutes(source.getMinutes());
        target.setSeconds(source.getSeconds());
    };

    const onSelectTime = (time: Date, name: "start" | "end") => {
        let val: any = v;
        let month: Date;
        if (name === "start") {
            month = store.currentMonth[0];
            if (val && val[0]) {
                copyHMS(val[0], time);
                if (val[0].getTime() > val[1].getTime()) {
                    val.reverse();
                    // dateTImeRange时需要切换currentMonth的时间
                    copyHMS(store.currentMonth[0], val[0]);
                    copyHMS(store.currentMonth[1], val[1]);
                } else {
                    copyHMS(month, time);
                }
                const vv = val.map((v: Date) => dayjs(v));
                value = vv;
                props.onchange?.(vv);
                ctx?.onFieldChange?.();
            } else {
                copyHMS(month, time);
            }
        } else if (name === "end") {
            month = store.currentMonth[1];
            if (val && val[1]) {
                copyHMS(val[1], time);
                if (val[0].getTime() > val[1].getTime()) {
                    val.reverse();
                    // dateTImeRange时需要切换currentMonth的时间
                    copyHMS(store.currentMonth[0], val[0]);
                    copyHMS(store.currentMonth[1], val[1]);
                } else {
                    copyHMS(month, time);
                }
                const vv = val.map((v: Date) => dayjs(v));
                value = vv;
                props.onchange?.(vv);
                ctx?.onFieldChange?.();
            } else {
                copyHMS(month, time);
            }
        } else {
            if (!val) {
                val = new Date();
            }
            copyHMS(val, time);
            month = store.currentMonth[0];
            copyHMS(month, time);
            value = dayjs(val);
            props.onchange?.(dayjs(val));
            ctx?.onFieldChange?.();
        }
        store.currentMonth = [...store.currentMonth];
    };

    /**
     * 是超出maxRange
     * @param start
     * @param current
     * @returns
     */
    const isOutRange = (start: Date, current: Date) => {
        if (props.maxRange) {
            const ms = start.getTime() - current.getTime();
            const days = Math.abs(ms / 1000 / 60 / 60 / 24);
            if (days > props.maxRange - 1) {
                return true;
            }
        }
        return false;
    };

    // 时间段选择
    const onMouseOver = (day: Date) => {
        if (store.range && store.range[0]) {
            if (isOutRange(store.range[0], day) && props.maxRange) {
                const end = new Date(store.range[0]);
                const delta = day.getTime() > store.range[0].getTime() ? 1 : -1;
                end.setDate(end.getDate() + (props.maxRange - 1) * delta);
                store.hoverDate = end;
                return;
            }
            store.hoverDate = new Date(day);
        }
    };

    const text = $derived.by(() => {
        const val = v;
        if (val) {
            if (typeof val === "string") {
                return { title: val };
            } else {
                if ((type === "dateRange" || type === "monthRange" || type === "dateTimeRange" || type === "yearRange" || type === "weekRange" || type === "quarterRange") && val instanceof Array) {
                    if (!val[0]) {
                        return [{ title: "" }, { title: "" }];
                    }
                    return [{ title: dayjs(val[0]).format(format) }, { title: dayjs(val[1]).format(format) }];
                }
                return { title: dayjs(val).format(format) };
            }
        }
        return { title: "" };
    });

    setContext(DatepickerContextKey, {
        onSelectDate,
        onMouseOver,
        disabledDate: props.disabledDate,
        onSelectTime,
        isVisible: () => visible,
    });
</script>

<div class={classList} style={props.style}>
    <Dropdown bind:visible transfer={props.transfer} {align} revers={props.revers} trigger="click" disabled={props.disabled}>
        {#snippet menu()}
            <div class={{ "cm-date-picker-wrap": true, "cm-date-picker-compact": props.compact, [`cm-date-picker-wrap-${type}`]: type }}>
                {#if props.shortCuts}
                    <div class="cm-date-picker-shortcuts">
                        {@render props.shortCuts?.()}
                    </div>
                {/if}
                {#if type === "date" || type === "week"}
                    <DatePane {store} stick={props.stick} {type} value={v} />
                {/if}
                {#if type === "month" || type === "year"}
                    <MonthPane {store} {type} value={v} />
                {/if}
                {#if type === "quarter"}
                    <QuarterPane {store} {type} value={v} />
                {/if}
                {#if type === "quarterRange"}
                    <QuarterRangePane {store} {type} value={v} stick={props.stick} />
                {/if}
                {#if type === "monthRange" || type === "yearRange"}
                    <MonthRangePane {store} {type} value={v} />
                {/if}
                {#if type === "dateRange" || type === "weekRange"}
                    <DateRangePane {store} stick={props.stick} value={v} {type} />
                {/if}
                {#if type === "dateTime"}
                    <DateTimePane {store} compact={props.compact} stick={props.stick} {type} value={v} {format} />
                {/if}
                {#if type === "dateTimeRange"}
                    <DateTimeRangePane {store} compact={props.compact} stick={props.stick} {type} value={v} {format} />
                {/if}
            </div>
        {/snippet}
        {#if !props.trigger}
            <Value
                prepend={props.prepend}
                {text}
                {onClear}
                clearable={props.clearable}
                range={type === "dateRange" || type === "monthRange" || type === "dateTimeRange" || type === "yearRange" || type === "weekRange" || type === "quarterRange"}
                placeholder={props.placeholder}
                disabled={props.disabled}
                size={props.size}
            >
                {#snippet icon()}
                    <Icon icon="calendar" />
                {/snippet}
            </Value>
        {:else}
            {@render props.trigger?.()}
        {/if}
    </Dropdown>
</div>
